package com.note.user.config;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.note.common.domain.File;
import com.note.common.dto.BucketPolicyConfigDto;
import com.note.common.dto.MinioUploadDto;
import com.note.common.dto.UserModifyDto;
import com.note.common.utils.FileDigest;
import com.note.common.utils.FileUtils;
import com.note.common.utils.TimeUtils;
import com.note.config.constants.GatewayConstants;
import com.note.user.dao.mapper.MinioMapper;
import com.note.user.service.internal.MinioFileService;
import io.minio.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * @projectName：wow-note
 * @package：com.note.user.config
 * @className：MinioConfig
 * @author：热伊木
 * @email：uyevan@163.com
 * @description：Minio文件上传对象
 * @date：2024-07-01 15:09
 * @version：1.0
 */
@Slf4j
@Configuration
public class MinioConfig {

    /*Minio源*/
    @Value("${minio.endpoint}")
    private String ENDPOINT;

    @Value("${minio.bucketName}")
    private String BUCKET_NAME;

    @Value("${minio.accessKey}")
    private String ACCESS_KEY;

    @Value("${minio.secretKey}")
    private String SECRET_KEY;

    @Value("${minio.file.extensions}")
    private String[] fileExtensions;

    @Value("${minio.oss-url}")
    private String OSS_URL;

    @Autowired
    private MinioMapper minioMapper;


    @Autowired
    private MinioFileService minioFileService;

    public MinioConfig() {
    }

    /**
     * 内部文件上传工具类
     */
    public void uploadFile(MultipartFile avatar,
                           UserModifyDto userModifyDto,
                           MinioUploadDto minioUploadDto,
                           InputStream inputStream) {
        try {
            String filename = avatar.getOriginalFilename();
            int index = filename.lastIndexOf(".");
            String etc = filename.substring(index + 1);//文件后缀
            long size = avatar.getSize();
            //TODO MD5验证
            String avatarMD5 = FileDigest.getFileMD5(FileUtils.transferToFile(avatar));
            File fileData = minioMapper.existsAvatarMd5(userModifyDto.getUsername(), avatarMD5);
            minioUploadDto.setFileMd5(avatarMD5);
            if (fileData != null) {
                minioUploadDto.setName(avatar.getName());
                minioUploadDto.setUrl(fileData.getUrl());
                log.info("文件已存在：{}", minioUploadDto.toString());
            } else {
                //TODO 链接客户端
                MinioClient minioClient = MinioClient.builder()
                        .endpoint(ENDPOINT)
                        .credentials(ACCESS_KEY, SECRET_KEY)
                        .build();

                //TODO 存储桶状态
                boolean bucketState = minioClient.bucketExists(BucketExistsArgs.builder().bucket(BUCKET_NAME).build());
                if (bucketState) {
                    log.info("存储桶已被创建过:{}", minioClient.listBuckets());
                } else {
                    //创建存储桶并设置只读权限
                    minioClient.makeBucket(MakeBucketArgs.builder().bucket(BUCKET_NAME).build());
                    BucketPolicyConfigDto policyConfigDto = createBucketPolicyConfigDto(BUCKET_NAME);
                    SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs.builder()
                            .bucket(BUCKET_NAME)
                            .config(JSONUtil.toJsonStr(policyConfigDto))
                            .build();
                    minioClient.setBucketPolicy(setBucketPolicyArgs);
                }
                //TODO 设置对象名称
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd", Locale.CHINA);
                //存储文件名
                String objectName = String.format("%s/%s-%s.%s", simpleDateFormat.format(new Date()),
                        IdUtil.simpleUUID(), Base64.encode(userModifyDto.getUsername()), etc);
                //TODO 上传文件
                PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                        .bucket(BUCKET_NAME)
                        .object(objectName)
                        .contentType(avatar.getContentType())
                        .stream(inputStream, size, (long) ObjectWriteArgs.MIN_MULTIPART_SIZE)
                        .build();
                minioClient.putObject(putObjectArgs);
                //TODO 响应信息（有误会报错）
                log.info("上传文件成功:{}", putObjectArgs);

                minioUploadDto.setName(objectName);
                minioUploadDto.setUrl(String.format("%s/%s/%s", OSS_URL, BUCKET_NAME, objectName));

                //TODO 调用RPC记录
                File recordFile = new File();
                recordFile.setUsername(userModifyDto.getUsername());
                recordFile.setFileName(minioUploadDto.getName());
                recordFile.setNoteId(Base64.encode(userModifyDto.getUsername()));
                recordFile.setUrl(minioUploadDto.getUrl());
                recordFile.setCreateTime(TimeUtils.getCurrentDate());
                recordFile.setFileMd5(minioUploadDto.getFileMd5());
                minioFileService.createFile(GatewayConstants.INTERNAL_KEY, recordFile);

                //释放
                inputStream.close();
            }
        } catch (Exception e) {
            log.error("文件上传失败:{}", e.getMessage());
        }
    }

    /**
     * 存储桶策略 --- 只读
     *
     * @param bucketName
     * @return
     */
    private BucketPolicyConfigDto createBucketPolicyConfigDto(String bucketName) {
        BucketPolicyConfigDto.Statement statement = BucketPolicyConfigDto.Statement.builder()
                .Effect("Allow")
                .Principal("*")
                .Action("s3:GetObject")
                .Resource("arn:aws:s3:::" + bucketName + "/*.**").build();
        return BucketPolicyConfigDto.builder()
                .version("2024-05-01")
                .statements(CollUtil.toList(statement))
                .build();
    }
}
